<?php
namespace Lavender\Dao;

use Lavender\Db\Exception;
use Lavender\Errno;

class PartRangeTable extends Table
{
    
    public static $table_range= [
        // 'kline_data_2019' => [0,20200000],
        // 'kline_data_2020' => [20200000,20210000],
        // 'kline_data_2021' => [20210000,20220000],
        // 'kline_data_2022' => [20220000,20230000],
        // 'kline_data_2023' => [20230000,99999999],
    ];
    
    // public static $defaultTable = null;

    public $range = null;

    public $range_list = null;


    public static function get_table_name_by_range_point($range){
        $count = count(static::$table_range);
        if($count < 1){
            throw new Exception(__CLASS__."table_range config is empty!");
        }
        foreach(static::$table_range as $table => $data){
            if($range >= $data[0] && $range < $data[1]){
                return $table;
            }
        }
        throw new Exception("range {$range} out of part table set!".json_encode(static::$table_range));
    }

    public static function get_table_name_by_range_limit($start,$end){
        $start_table = static::get_table_name_by_range_point($start);
        $end_table = static::get_table_name_by_range_point($end);
        if($start_table == $end_table){
            return [$start_table];
        }
        $tables = array_keys(static::$table_range);
        $start_index = array_search($start_table,$tables);
        $end_index = array_search($end_table,$tables);
        return array_slice($tables,$start_index,$end_index -  $start_index + 1);
    }

    /**
     * @param integer $range
     */
    public function set_range($range) {
        $this->range = intval($range);
        $this->range_list =  null;
        return $this;
    }

    /**
     * @param integer $start
     * @param integer $end
     */
    public function set_range_list($start,$end) {
        $this->range = null;
        $this->range_list = [intval($start),intval($end)];
        return $this;
    }

    public function get_table_list(){
        if($this->range != null){
            return [static::get_table_name_by_range_point($this->range)];
        }
        if($this->range_list != null){
            return static::get_table_name_by_range_limit($this->range_list[0],$this->range_list[1]);
        }
        return [];
    }

    /**
     * @param \Lavender\Db\Mysql $db
     * @param string|null $condition
     * @param array $fields
     * @param string $order
     * @param int $offset
     * @param int $length
     * @param string $group
     * @param string $having
     * @return string
     */
    public function get_union_all_sql(\Lavender\Db\Mysql $db, $condition,$fields = array(), $order = null ,$offset = null, $length = null, $group = null, $having = null){
        $table_list = $this->get_table_list();
        $table_count = count($table_list);
        if($table_count < 1){
            throw new Exception("get range table is empty!");
        }
        $sub_sql = [];
        $field_str = '';
        $group_str = '';
        $order_str = '';
        $where = '';
        foreach($table_list as $table){
            if (!$db->check_name($table)) {
                throw new Exception("table name '{$table}' invalid", Errno::PARAM_INVALID);
            }
            $field_str = $db->get_field_str($fields);
            $where = $condition ? " WHERE {$condition}" : '';
            $group_str = $db->get_group_str($group,$having);
            $order_str = $db->get_order_str($order);
            $limit_str = $db->get_limit_sql($offset,$length);
            $sub_sql[] = "SELECT {$field_str} FROM `{$table}`{$where}{$order_str}{$limit_str}";
        }
        if(count($sub_sql) == 1){
            return "SELECT {$field_str} FROM `{$table}`{$where}{$group_str}{$order_str}{$limit_str}";
        }
        else{
            $sub_sql_str = '(' .implode(') UNION ALL (',$sub_sql).')';
            return "SELECT {$field_str} FROM ($sub_sql_str) as a {$group_str}{$order_str}{$limit_str}";
        } 

    }

    /**
     * multipie get
     *
     * @param mixed $ids id or id list
     * @param array $fields
     * @param array $filter
     * @param string $order
     * @param int $offset
     * @param int $length
     *
     * @return array
     */
    public function get($ids, $fields = array(), array $filter = array(), $order = null, $offset = null, $length = null,$group  = null,$having  = null)
    {
        $db = $this->get_database_instance();
        $condition = $this->build_condition($ids, $filter,$db);
        $sql = $this->get_union_all_sql($db,$condition, $fields, $order ,$offset, $length, $group, $having);

        return $db->get_by_sql($sql);
    }


    /**
     * single record get
     * @param $id
     * @param array $filter $filter filter condition
     * @param null $order
     * @return mixed
     * @throws Exception
     */
    public function get_single($id, array $filter = array(), $order = null)
    {
        $this->check_id($id);
        if (is_null($id) && empty($filter)) {
            throw new Exception("id and filter is empty", Errno::PARAM_INVALID);
        }        
        $items = $this->get($id,[],$filter,$order,1);
        if (empty($items)) {
            return null;
        }
        return $items[0];
    }

    /**
     * single record get
     * @param array $filter $filter filter condition
     * @param null $order
     * @return mixed
     * @throws Exception
     */
    public function get_one(array $filter,array $fields = array(), $order = null)
    {
        if (empty($filter)) {
            throw new Exception("filter is empty", Errno::PARAM_INVALID);
        }
        $items = $this->get(null,$fields,$filter,$order,1);
        if (empty($items)) {
            return null;
        }
        return $items[0];        
    }

    /**
     * insert record
     * @param $id
     * @param array $record
     * @return boolean
     * @throws Exception
     */
    public function add($id, array $record)
    {
        $table_list = $this->get_table_list();
        $table_count = count($table_list);
        if($table_count > 1){
            throw new Exception("add  but more than one table fetch!");
        }
        $table = $table_list[0];
        if(empty($table)){
            throw new Exception("add  but more table name is empty!");
        }
        $this->set_table_name($table);
        return parent::add($id,$record);
    }


    /**
     * insert_or_update a record
     * @param $id
     * @param array $record
     * @param  array $update
     * @return bool
     * @throws Exception
     * @throws \Lavender\Db\Exception
     */
    public function upset($id, array $record,array $update)
    {
        $table_list = $this->get_table_list();
        $table_count = count($table_list);
        if($table_count > 1){
            throw new Exception("upset  but more than one table fetch!");
        }
        $table = $table_list[0];
        if(empty($table)){
            throw new Exception("upset  but more table name is empty!");
        }
        $this->set_table_name($table);
        return parent::upset($id,$record,$update);
    }

    /**
     * set to a record
     * @param $id
     * @param array $record
     * @return bool
     * @throws Exception
     * @throws \Lavender\Db\Exception
     */
    public function set($id, array $record)
    {
        $table_list = $this->get_table_list();
        $table_count = count($table_list);
        if($table_count > 1){
            throw new Exception("upset  but more than one table fetch!");
        }
        $table = $table_list[0];
        if(empty($table)){
            throw new Exception("upset  but more table name is empty!");
        }
        $this->set_table_name($table);
        return parent::set($id,$record);
    }

    /**
     * update a record
     * @param $id
     * @param $record
     * @param array $filter
     * @return int
     * @throws Exception
     * @throws \Lavender\Db\Exception
     */
    public function update($id, $record, array $filter = array())
    {
        $this->check_id($id);
        $db = $this->get_database_instance($id);

        $condition = $this->build_condition($id, $filter,$db);

        $table_list = $this->get_table_list();
        $table_count = count($table_list);
        if($table_count < 1){
            throw new Exception("update  but more table name is empty!");
        }
        $result = 0;
        foreach( $table_list as $table){            
            $db->update($table, $record, $condition);
            $result += $db->get_affected_rows();
        }
        
        //数据同步
        static::set_sync_data(get_called_class(), __FUNCTION__, func_get_args());
        return $result;
    }

    /**
     * @param $ids
     * @param $record
     * @param array $filter
     * @return int
     * @throws Exception
     * @throws \Lavender\Db\Exception
     */
    public function update_all($ids,$record,array $filter = array())
    {
        if(empty($record))
        {
            return 0;
        }
        $db = $this->get_database_instance();
        $condition = $this->build_condition($ids, $filter,$db);

        $table_list = $this->get_table_list();
        $table_count = count($table_list);
        if($table_count < 1){
            throw new Exception("update_all  but more table name is empty!");
        }
        $result = 0;
        foreach( $table_list as $table){            
            $db->update($table, $record, $condition);
            $result += $db->get_affected_rows();
        }

        //数据同步
        static::set_sync_data(get_called_class(), __FUNCTION__, func_get_args());
        return $result;
    }

    public function bulk_insert(array $items,$piece = 300){
        $table_list = $this->get_table_list();
        $table_count = count($table_list);
        if($table_count > 1){
            throw new Exception("bulk_insert  but more than one table fetch!");
        }
        $table = $table_list[0];
        if(empty($table)){
            throw new Exception("bulk_insert  but more table name is empty!");
        }
        $this->set_table_name($table);
        return parent::bulk_insert($items,$piece);
    }

    
    /**
     * count
     * @param $id
     * @param array $filter
     * @param $field
     * @return int
     * @throws Exception
     */
    public function count($id, array $filter = array(),$field = '*')
    {
        $table_list = $this->get_table_list();
        $table_count = count($table_list);
        if($table_count > 1){
            throw new Exception("count  but more than one table fetch!");
        }
        $table = $table_list[0];
        if(empty($table)){
            throw new Exception("count  but more table name is empty!");
        }
        $this->set_table_name($table);
        return parent::count($id,$filter,$field);
    }

    /**
     * @param $id
     * @param $increments
     * @param array $update_item
     * @param array $create_item
     * @return int
     * @throws Exception
     * @throws \Lavender\Db\Exception
     */
    public function increment($id, array $increments = [], array $update_item = [] , array $create_item = []){
        $table_list = $this->get_table_list();
        $table_count = count($table_list);
        if($table_count > 1){
            throw new Exception("increment  but more than one table fetch!");
        }
        $table = $table_list[0];
        if(empty($table)){
            throw new Exception("increment  but more table name is empty!");
        }
        $this->set_table_name($table);
        return parent::increment($id, $increments ,$update_item , $create_item);
    }

    /**
     * @param $ids
     * @param array $filter
     * @return int
     * @throws Exception
     * @throws \Lavender\Db\Exception
     */
    public function delete_all($ids, array $filter = array())
    {        
        $db = $this->get_database_instance();
        $condition = $this->build_condition($ids, $filter,$db);
        if(empty($condition))
        {
            throw new Exception('delete all condition can not be empty', Errno::PARAM_INVALID);
        }
        $table_list = $this->get_table_list();
        $table_count = count($table_list);
        if($table_count < 1) {
            throw new Exception("delete_all but table is empty!");
        }
        $result = 0;
        foreach($table_list as $table){
            $db->delete($table, $condition);
            //return $this->get_database_instance($id)->get_affected_rows();
            $result +=  $db->get_affected_rows();
        }
        //数据同步
        static::set_sync_data(get_called_class(), __FUNCTION__, func_get_args());
        return $result;
    }

    /**
     * delete
     * @param $id
     * @param array $filter
     * @return int
     * @throws Exception
     * @throws \Lavender\Db\Exception
     */
    public function delete($id, array $filter = array())
    {
        $this->check_id($id);
        $db = $this->get_database_instance($id);
        $condition = $this->build_condition($id, $filter,$db);

        $db->delete($this->get_table_name($id), $condition);
        $table_list = $this->get_table_list();
        $table_count = count($table_list);
        if($table_count < 1) {
            throw new Exception("delete_all but table is empty!");
        }
        $result = 0;
        foreach($table_list as $table){
            $db->delete($table, $condition);
            //return $this->get_database_instance($id)->get_affected_rows();
            $result +=  $db->get_affected_rows();
        }     
        //数据同步
        static::set_sync_data(get_called_class(), __FUNCTION__, func_get_args());
        return $result;
    }
}

